#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
性能监控中间件

这个模块包含性能监控中间件：
1. 监控请求处理时间
2. 监控内存使用情况
3. 监控数据库查询性能
4. 收集性能指标
5. 生成性能报告

提供详细的性能分析和优化建议
支持性能阈值告警

作者: FastAPI 学习项目
创建时间: 2024
"""

import time
import psutil
import asyncio
from typing import Callable, Dict, List, Optional
from fastapi import Request, Response
from starlette.middleware.base import BaseHTTPMiddleware
from loguru import logger
from collections import defaultdict, deque
from datetime import datetime, timedelta
import threading
import json


class PerformanceMetrics:
    """
    性能指标收集器
    
    收集和存储各种性能指标数据。
    """
    
    def __init__(self, max_history: int = 1000):
        """
        初始化性能指标收集器
        
        Args:
            max_history: 最大历史记录数量
        """
        self.max_history = max_history
        self.request_times = deque(maxlen=max_history)
        self.memory_usage = deque(maxlen=max_history)
        self.cpu_usage = deque(maxlen=max_history)
        self.endpoint_stats = defaultdict(lambda: {
            'count': 0,
            'total_time': 0,
            'min_time': float('inf'),
            'max_time': 0,
            'errors': 0
        })
        self.slow_requests = deque(maxlen=100)  # 保存最慢的100个请求
        self.error_requests = deque(maxlen=100)  # 保存最近的100个错误请求
        self._lock = threading.Lock()
    
    def add_request_metric(
        self,
        endpoint: str,
        method: str,
        duration: float,
        status_code: int,
        memory_usage: float,
        cpu_usage: float
    ):
        """
        添加请求指标
        
        Args:
            endpoint: 端点路径
            method: HTTP方法
            duration: 请求处理时间
            status_code: 响应状态码
            memory_usage: 内存使用量
            cpu_usage: CPU使用率
        """
        with self._lock:
            # 记录请求时间
            self.request_times.append({
                'timestamp': datetime.utcnow(),
                'duration': duration,
                'endpoint': endpoint,
                'method': method,
                'status_code': status_code
            })
            
            # 记录系统资源使用
            self.memory_usage.append({
                'timestamp': datetime.utcnow(),
                'usage': memory_usage
            })
            
            self.cpu_usage.append({
                'timestamp': datetime.utcnow(),
                'usage': cpu_usage
            })
            
            # 更新端点统计
            key = f"{method} {endpoint}"
            stats = self.endpoint_stats[key]
            stats['count'] += 1
            stats['total_time'] += duration
            stats['min_time'] = min(stats['min_time'], duration)
            stats['max_time'] = max(stats['max_time'], duration)
            
            if status_code >= 400:
                stats['errors'] += 1
                # 记录错误请求
                self.error_requests.append({
                    'timestamp': datetime.utcnow(),
                    'endpoint': endpoint,
                    'method': method,
                    'status_code': status_code,
                    'duration': duration
                })
            
            # 记录慢请求（超过1秒）
            if duration > 1.0:
                self.slow_requests.append({
                    'timestamp': datetime.utcnow(),
                    'endpoint': endpoint,
                    'method': method,
                    'duration': duration,
                    'status_code': status_code
                })
    
    def get_summary_stats(self) -> Dict:
        """
        获取汇总统计信息
        
        Returns:
            Dict: 汇总统计信息
        """
        with self._lock:
            if not self.request_times:
                return {}
            
            # 计算请求时间统计
            durations = [req['duration'] for req in self.request_times]
            durations.sort()
            
            total_requests = len(durations)
            avg_duration = sum(durations) / total_requests
            
            # 计算百分位数
            p50_idx = int(total_requests * 0.5)
            p90_idx = int(total_requests * 0.9)
            p95_idx = int(total_requests * 0.95)
            p99_idx = int(total_requests * 0.99)
            
            # 计算错误率
            error_count = sum(1 for req in self.request_times if req['status_code'] >= 400)
            error_rate = (error_count / total_requests) * 100 if total_requests > 0 else 0
            
            # 计算内存和CPU使用情况
            if self.memory_usage:
                avg_memory = sum(m['usage'] for m in self.memory_usage) / len(self.memory_usage)
                max_memory = max(m['usage'] for m in self.memory_usage)
            else:
                avg_memory = max_memory = 0
            
            if self.cpu_usage:
                avg_cpu = sum(c['usage'] for c in self.cpu_usage) / len(self.cpu_usage)
                max_cpu = max(c['usage'] for c in self.cpu_usage)
            else:
                avg_cpu = max_cpu = 0
            
            return {
                'total_requests': total_requests,
                'error_count': error_count,
                'error_rate': round(error_rate, 2),
                'avg_response_time': round(avg_duration, 4),
                'min_response_time': round(min(durations), 4),
                'max_response_time': round(max(durations), 4),
                'p50_response_time': round(durations[p50_idx], 4) if p50_idx < total_requests else 0,
                'p90_response_time': round(durations[p90_idx], 4) if p90_idx < total_requests else 0,
                'p95_response_time': round(durations[p95_idx], 4) if p95_idx < total_requests else 0,
                'p99_response_time': round(durations[p99_idx], 4) if p99_idx < total_requests else 0,
                'avg_memory_usage': round(avg_memory, 2),
                'max_memory_usage': round(max_memory, 2),
                'avg_cpu_usage': round(avg_cpu, 2),
                'max_cpu_usage': round(max_cpu, 2),
                'slow_requests_count': len(self.slow_requests)
            }
    
    def get_endpoint_stats(self) -> Dict:
        """
        获取端点统计信息
        
        Returns:
            Dict: 端点统计信息
        """
        with self._lock:
            result = {}
            for endpoint, stats in self.endpoint_stats.items():
                if stats['count'] > 0:
                    result[endpoint] = {
                        'count': stats['count'],
                        'avg_time': round(stats['total_time'] / stats['count'], 4),
                        'min_time': round(stats['min_time'], 4),
                        'max_time': round(stats['max_time'], 4),
                        'error_count': stats['errors'],
                        'error_rate': round((stats['errors'] / stats['count']) * 100, 2)
                    }
            return result
    
    def get_slow_requests(self, limit: int = 10) -> List[Dict]:
        """
        获取慢请求列表
        
        Args:
            limit: 返回数量限制
            
        Returns:
            List[Dict]: 慢请求列表
        """
        with self._lock:
            # 按持续时间排序
            sorted_requests = sorted(
                self.slow_requests, 
                key=lambda x: x['duration'], 
                reverse=True
            )
            return list(sorted_requests[:limit])
    
    def clear_metrics(self):
        """
        清空所有指标数据
        """
        with self._lock:
            self.request_times.clear()
            self.memory_usage.clear()
            self.cpu_usage.clear()
            self.endpoint_stats.clear()
            self.slow_requests.clear()
            self.error_requests.clear()


class PerformanceMiddleware(BaseHTTPMiddleware):
    """
    性能监控中间件
    
    监控请求处理性能和系统资源使用情况。
    """
    
    def __init__(
        self,
        app,
        *,
        slow_request_threshold: float = 1.0,
        memory_threshold: float = 80.0,
        cpu_threshold: float = 80.0,
        enable_detailed_logging: bool = True
    ):
        """
        初始化性能监控中间件
        
        Args:
            app: FastAPI 应用实例
            slow_request_threshold: 慢请求阈值（秒）
            memory_threshold: 内存使用阈值（百分比）
            cpu_threshold: CPU使用阈值（百分比）
            enable_detailed_logging: 是否启用详细日志
        """
        super().__init__(app)
        self.slow_request_threshold = slow_request_threshold
        self.memory_threshold = memory_threshold
        self.cpu_threshold = cpu_threshold
        self.enable_detailed_logging = enable_detailed_logging
        self.metrics = PerformanceMetrics()
        
        # 启动后台监控任务
        self._start_background_monitoring()
    
    async def dispatch(self, request: Request, call_next: Callable) -> Response:
        """
        处理请求并收集性能指标
        
        Args:
            request: HTTP请求对象
            call_next: 下一个中间件或路由处理器
            
        Returns:
            Response: HTTP响应对象
        """
        # 记录开始时间和系统状态
        start_time = time.time()
        start_memory = self._get_memory_usage()
        start_cpu = self._get_cpu_usage()
        
        # 处理请求
        response = await call_next(request)
        
        # 计算处理时间和资源使用
        duration = time.time() - start_time
        end_memory = self._get_memory_usage()
        end_cpu = self._get_cpu_usage()
        
        # 记录指标
        self.metrics.add_request_metric(
            endpoint=request.url.path,
            method=request.method,
            duration=duration,
            status_code=response.status_code,
            memory_usage=end_memory,
            cpu_usage=end_cpu
        )
        
        # 添加性能头部
        response.headers["X-Response-Time"] = f"{duration:.4f}s"
        response.headers["X-Memory-Usage"] = f"{end_memory:.2f}%"
        
        # 检查性能阈值
        await self._check_performance_thresholds(
            request, duration, end_memory, end_cpu, response.status_code
        )
        
        # 详细日志记录
        if self.enable_detailed_logging:
            self._log_request_performance(
                request, duration, end_memory, end_cpu, response.status_code
            )
        
        return response
    
    def _get_memory_usage(self) -> float:
        """
        获取内存使用率
        
        Returns:
            float: 内存使用率（百分比）
        """
        try:
            return psutil.virtual_memory().percent
        except Exception:
            return 0.0
    
    def _get_cpu_usage(self) -> float:
        """
        获取CPU使用率
        
        Returns:
            float: CPU使用率（百分比）
        """
        try:
            return psutil.cpu_percent(interval=None)
        except Exception:
            return 0.0
    
    async def _check_performance_thresholds(
        self,
        request: Request,
        duration: float,
        memory_usage: float,
        cpu_usage: float,
        status_code: int
    ):
        """
        检查性能阈值并发出告警
        
        Args:
            request: HTTP请求对象
            duration: 请求处理时间
            memory_usage: 内存使用率
            cpu_usage: CPU使用率
            status_code: 响应状态码
        """
        warnings = []
        
        # 检查慢请求
        if duration > self.slow_request_threshold:
            warnings.append(f"慢请求检测: {duration:.4f}s > {self.slow_request_threshold}s")
        
        # 检查内存使用
        if memory_usage > self.memory_threshold:
            warnings.append(f"高内存使用: {memory_usage:.2f}% > {self.memory_threshold}%")
        
        # 检查CPU使用
        if cpu_usage > self.cpu_threshold:
            warnings.append(f"高CPU使用: {cpu_usage:.2f}% > {self.cpu_threshold}%")
        
        # 记录告警
        if warnings:
            logger.warning(
                "性能告警",
                extra={
                    "path": request.url.path,
                    "method": request.method,
                    "duration": duration,
                    "memory_usage": memory_usage,
                    "cpu_usage": cpu_usage,
                    "status_code": status_code,
                    "warnings": warnings
                }
            )
    
    def _log_request_performance(
        self,
        request: Request,
        duration: float,
        memory_usage: float,
        cpu_usage: float,
        status_code: int
    ):
        """
        记录请求性能日志
        
        Args:
            request: HTTP请求对象
            duration: 请求处理时间
            memory_usage: 内存使用率
            cpu_usage: CPU使用率
            status_code: 响应状态码
        """
        logger.info(
            "请求性能",
            extra={
                "event_type": "request_performance",
                "path": request.url.path,
                "method": request.method,
                "duration": round(duration, 4),
                "memory_usage": round(memory_usage, 2),
                "cpu_usage": round(cpu_usage, 2),
                "status_code": status_code,
                "query_params": dict(request.query_params),
                "user_agent": request.headers.get("user-agent", ""),
                "timestamp": datetime.utcnow().isoformat()
            }
        )
    
    def _start_background_monitoring(self):
        """
        启动后台监控任务
        """
        def monitor_system():
            while True:
                try:
                    # 每分钟记录一次系统状态
                    memory_usage = self._get_memory_usage()
                    cpu_usage = self._get_cpu_usage()
                    
                    logger.info(
                        "系统监控",
                        extra={
                            "event_type": "system_monitoring",
                            "memory_usage": round(memory_usage, 2),
                            "cpu_usage": round(cpu_usage, 2),
                            "timestamp": datetime.utcnow().isoformat()
                        }
                    )
                    
                    # 检查系统资源告警
                    if memory_usage > self.memory_threshold:
                        logger.warning(f"系统内存使用过高: {memory_usage:.2f}%")
                    
                    if cpu_usage > self.cpu_threshold:
                        logger.warning(f"系统CPU使用过高: {cpu_usage:.2f}%")
                    
                    time.sleep(60)  # 每分钟检查一次
                    
                except Exception as e:
                    logger.error(f"系统监控异常: {e}")
                    time.sleep(60)
        
        # 在后台线程中运行监控
        monitor_thread = threading.Thread(target=monitor_system, daemon=True)
        monitor_thread.start()
    
    def get_metrics(self) -> Dict:
        """
        获取性能指标
        
        Returns:
            Dict: 性能指标数据
        """
        return {
            "summary": self.metrics.get_summary_stats(),
            "endpoints": self.metrics.get_endpoint_stats(),
            "slow_requests": self.metrics.get_slow_requests()
        }
    
    def reset_metrics(self):
        """
        重置性能指标
        """
        self.metrics.clear_metrics()
        logger.info("性能指标已重置")


class PerformanceAnalyzer:
    """
    性能分析器
    
    提供性能数据分析和报告功能。
    """
    
    def __init__(self, metrics: PerformanceMetrics):
        """
        初始化性能分析器
        
        Args:
            metrics: 性能指标收集器
        """
        self.metrics = metrics
    
    def generate_performance_report(self) -> Dict:
        """
        生成性能报告
        
        Returns:
            Dict: 性能报告
        """
        summary = self.metrics.get_summary_stats()
        endpoints = self.metrics.get_endpoint_stats()
        slow_requests = self.metrics.get_slow_requests()
        
        # 分析最慢的端点
        slowest_endpoints = sorted(
            endpoints.items(),
            key=lambda x: x[1]['avg_time'],
            reverse=True
        )[:5]
        
        # 分析错误率最高的端点
        error_prone_endpoints = sorted(
            endpoints.items(),
            key=lambda x: x[1]['error_rate'],
            reverse=True
        )[:5]
        
        # 生成建议
        recommendations = self._generate_recommendations(summary, endpoints)
        
        return {
            "generated_at": datetime.utcnow().isoformat(),
            "summary": summary,
            "slowest_endpoints": dict(slowest_endpoints),
            "error_prone_endpoints": dict(error_prone_endpoints),
            "recent_slow_requests": slow_requests,
            "recommendations": recommendations
        }
    
    def _generate_recommendations(self, summary: Dict, endpoints: Dict) -> List[str]:
        """
        生成性能优化建议
        
        Args:
            summary: 汇总统计
            endpoints: 端点统计
            
        Returns:
            List[str]: 优化建议列表
        """
        recommendations = []
        
        # 检查平均响应时间
        if summary.get('avg_response_time', 0) > 1.0:
            recommendations.append("平均响应时间较长，建议优化数据库查询和业务逻辑")
        
        # 检查错误率
        if summary.get('error_rate', 0) > 5.0:
            recommendations.append("错误率较高，建议检查错误日志并修复相关问题")
        
        # 检查内存使用
        if summary.get('max_memory_usage', 0) > 80.0:
            recommendations.append("内存使用率较高，建议优化内存使用或增加服务器内存")
        
        # 检查CPU使用
        if summary.get('max_cpu_usage', 0) > 80.0:
            recommendations.append("CPU使用率较高，建议优化计算密集型操作或增加服务器CPU")
        
        # 检查慢请求
        if summary.get('slow_requests_count', 0) > 10:
            recommendations.append("存在较多慢请求，建议分析慢请求日志并进行优化")
        
        # 检查端点性能
        for endpoint, stats in endpoints.items():
            if stats['avg_time'] > 2.0:
                recommendations.append(f"端点 {endpoint} 响应时间较长，建议进行优化")
            
            if stats['error_rate'] > 10.0:
                recommendations.append(f"端点 {endpoint} 错误率较高，建议检查相关代码")
        
        return recommendations